'''
This is TwistedSim script that shows the user how to emulate our 
simple-torrent application under TwistedSim using process-based emulation.
'''


from twistedSim.simManager import SimManager, Machine, IpInterface
from twistedSim.moduleHandler import twistedH
from twistedSim.contextHandlers import ProcessHandler

import os
from os import path
import shutil
import struct
from random import randrange

#The following assigned values are the default assigned values if the user didn't
#specify in the value of the parameter in sys.argv.
#Change these value to change default values.
torrentName = "Lawrence_ext.tor"
sharedFileName = "Camel - Lawrence.flv"
seederStartTime = 10

numberOfLeechers = 25
leecherStartTime = 60 #The start time for first leechers
leecherTimeDelta = 120 #The difference between two leechers
peersDeathInterval = 10*60  #Death cycle interval
peesrDeathRatio = 20.00/100  #Death ratio of peers per cycle

scriptStopTime = 1*60*60#The simulation stop time from the time of the last
                        # leecher is created in.

#The following are global data for internal use, bad design: should not
# use globals.
leechers = [] #All alive leechers will be kept here,
trackerFiles = ["__init__.py", "tracker.py", "util.py"] #copy files to directory
#Both of the next 2 lists are reassigned in parseArgs()
leecherFiles = ["__init__.py", "peer.py", "torrentFile.py",
                "peerToTracker.py", "util.py"]# + torrentName] #not assigned yet
seederFiles = leecherFiles #+ [sharedFileName] #sharedFileName not assigned yet


def parseArgs():
    global torrentName, sharedFileName, numberOfLeechers
    global leecherFiles, seederFiles, peersDeathInterval, peesrDeathRatio
    global seederStartTime, leecherStartTime, leecherTimeDelta
    from sys import argv
    from twisted.python import usage
    
    class ParseOptions(usage.Options):
        optParameters = [
     ["torrent", "t", torrentName, "The torrent file name"],
     ["fileShared", "f", sharedFileName, "Name of the file to be shared"],
     ["nLeechers", "n", numberOfLeechers, "Number of leechers to be created", int],
     ["seederTime", "s", seederStartTime, "The start time (from simulation start) of the only seeder", float],
     ["leecherTime", "l", leecherStartTime, "The start time (from simulation start) of the first leecher", float],
     ["leecherInterval", "i", leecherTimeDelta,"Time interval between two created leechers", float],
     ["deathInterval","d",peersDeathInterval, "The time interval between two death cycles", float],
     ["deathRatio", "r" , peesrDeathRatio,"The ratio of the alive peers that will be killed per death cycle", float]]
    opts = ParseOptions()
    opts.parseOptions()
    if len(argv) == 1:
        print "Script: Using default values, for help type:"
        print argv[0] + " --help"
    else:
        torrentName = opts.opts["torrent"]
        sharedFileName = opts.opts["fileShared"]
        numberOfLeechers = opts.opts["nLeechers"]
        seederStartTime = opts.opts["seederTime"]
        leecherStartTime = opts.opts["leecherTime"]
        leecherTimeDelta = opts.opts["leecherInterval"]
        peersDeathInterval = opts.opts["deathInterval"]
        peersDeathRatio = opts.opts["deathRatio"]              
    leecherFiles = leecherFiles + [torrentName]
    seederFiles = leecherFiles + [sharedFileName]


def createTracker():
    trackerName = "Tracker"
    trackerPath = createPeerDir(trackerName, trackerFiles)
    tracker = ProcessHandler("tracker.py", twistedH(), trackerPath,
                      startTime = 0, nickName = trackerName)
    trackerMachine = Machine()
    trackerIP = IpInterface("34.131.24.213", trackerMachine)
    SimManager.addModule(tracker, trackerMachine)
    
def createSeeder():
    seederName = "Seeder"
    seederPath = createPeerDir(seederName, seederFiles)
    seeder = ProcessHandler("peer.py", twistedH(), seederPath, 
                    sysArgs = torrentName, startTime = seederStartTime,
                    nickName = seederName)
    seederMachine = Machine()
    seederIP = IpInterface("21.214.9.1", seederMachine)
    SimManager.addModule(seeder, seederMachine)

def createPeerDir(peerName, listOfFiles):
    os.mkdir(peerName)
    peerPath =  path.realpath(".") + "/" + peerName
    map(shutil.copy, listOfFiles, [peerPath]*len(listOfFiles))
    return peerPath

def createLeechers():
    #Timing stuff:
    global leecherStartTime, leecherTimeDelta, leechers
    #IP Stuff:
    ipPrefix = "121"
    ipSecondPart = 7 
    ipThirdPart = 31
    ipFourthPart = 70
    
    for leecherCount in range(numberOfLeechers):
        leecherName = "Leecher" + str(leecherCount)
        #Create leecher's directory:
        leecherPath = createPeerDir(leecherName, leecherFiles)
        #Create leecher's module:
        leecher = ProcessHandler("peer.py", twistedH(), leecherPath, 
                         sysArgs = torrentName, 
                         startTime = leecherStartTime+ leecherTimeDelta,
                         nickName = leecherName)
        leechers.append(leecher)
        leecherStartTime += leecherTimeDelta
        #Create machine:
        leecherMachine = Machine()
        SimManager.addModule(leecher, leecherMachine)
        #Create IP interface:
        ipStr = map(lambda intIp: str(intIp),[ipSecondPart, ipThirdPart,
                                               ipFourthPart]) #Convert int to str
        ip = str.join(".", ([ipPrefix] + ipStr)) #Create the actual ip
        leecherIP = IpInterface(ip,leecherMachine)
        if ipFourthPart == 255:
            ipFourthPart = 0
            if ipThirdPart == 255:
                ipSecondPart += 1
                ipThirdPart = 0
            else:
                ipThirdPart += 1 
        else:
            ipFourthPart += 1

def randomDeath():
    def serialKiller():
        aliveNodes = filter(lambda x:x.isRunning(), leechers)
        numOfDeaths = int(len(aliveNodes) * peesrDeathRatio)
        #print "Number of deaths: " , numOfDeaths, "\tAlive: ", len(aliveNodes)
        for i in range(numOfDeaths):
            foundAliveVictim = False
            while not foundAliveVictim:
                victimIndex = randrange(len(leechers))
                unluckyPeer = leechers[victimIndex]
                if unluckyPeer.isRunning():
                    foundAliveVictim = True
                    leechers.pop(victimIndex)
            unluckyPeer.die()
    #Avoid killing on our first run, the simulation might haven't yet
    # started
    SimManager.callLater(peersDeathInterval, serialKiller)
    SimManager.callLater(peersDeathInterval, randomDeath)

def setStopTime():
    baseStopTime = leecherStartTime + (leecherTimeDelta*numberOfLeechers)
    stopTime = baseStopTime + scriptStopTime
    def stopSim():
        SimManager.stop = True
    SimManager.callLater(stopTime, stopSim)


def trackTime():
    import time
    from twistedSim.overrideManager import OverrideManager
    realAscTime = OverrideManager.getEntity("time.asctime")
    realTime = OverrideManager.getEntity("time.time")
    startTime = time.time()
    def concludeTime():
        print "SimpleTorrent: " 
        print "Peers: ", numberOfLeechers
        fullPath = os.path.realpath(".") + "/" + sharedFileName
        normedPath = os.path.normpath(fullPath)
        print "FileSizeKb: ", os.path.getsize(normedPath)/1024.0
        print "Leecher Interval: ", leecherTimeDelta
        print "Death Interval: ", peersDeathInterval
        print "Death Ratio: ", peesrDeathRatio
        print "Time now: " + time.asctime()
        print "Time since i started: " + str(time.time() - startTime)
        print "Real TIme: " + realAscTime()
        print "Real time since i started: " + str(realTime() - startTime)
        print "Simulated - Real = " + str(time.time() - realTime())    
    SimManager.addPostRun(concludeTime)



def main():
    parseArgs()
    createTracker()
    createSeeder()
    createLeechers()
    randomDeath()
    setStopTime()
    trackTime()
    SimManager.run()
    
    
if __name__ == '__main__':
    main()
